﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;

public class BRTParser
{
    private List<BRTBone> boneList;
    private Dictionary<string, string> Hierachy;
    public class BRTBone
    {
        public string name;
        public List<BRTBone> children;
        public float offsetX, offsetY, offsetZ;
    }

    public frame tpose_frame;
    public List<frame> frames;

    public class frame
    {
        public Dictionary<string, Vector3> euler;
        public Dictionary<string, Quaternion> localRot;
    }


    private BRTBone getBoneByName(string name)
    {
        foreach (BRTBone bb in boneList)
        {
            if (bb.name.Equals(name))
            {
                return bb;
            }
        }
        return null;
    }

    public void readBrt(string filename)
    {
        // init
        Hierachy = new Dictionary<string, string>();
        tpose_frame = new frame();
        frames = new List<frame>();
        boneList = new List<BRTBone>();

        using (StreamReader sr = new StreamReader(filename))
        {
            string line = sr.ReadLine();
            int bonesCount = Int32.Parse(line.Split(':')[1]);

            // Hierachy and offset
            BRTBone hips = new BRTBone();
            hips.name = "Hips"; // hips
            hips.offsetX = 0f;
            hips.offsetY = 0f;
            hips.offsetZ = 0f;
            boneList.Add(hips);

            if (sr.ReadLine().Equals("Hierachy:"))
            {
                for (int i = 0; i < bonesCount - 1; i++)
                {
                    line = sr.ReadLine();
                    string[] ts = line.Split(' ');
                    if (ts.Length == 5)
                    {
                        Hierachy.Add(ts[0], ts[1]);
                        BRTBone tb = new BRTBone();
                        tb.name = ts[0];
                        tb.offsetX = float.Parse(ts[2]);
                        tb.offsetY = float.Parse(ts[3]);
                        tb.offsetZ = float.Parse(ts[4]);
                        boneList.Add(tb);
                    }
                }
            }

            // Children
            foreach (BRTBone bb in boneList)
                bb.children = new List<BRTBone>();
            foreach (BRTBone bb in boneList)
            {
                foreach (KeyValuePair<string, string> kvp in Hierachy)
                {
                    if (kvp.Value.Equals(bb.name))
                    {
                        bb.children.Add(getBoneByName(kvp.Key));
                    }
                }
            }

            // tpose_frame
            if (sr.ReadLine().Equals("Tpose:"))
            {
                Dictionary<string, Vector3> dv = new Dictionary<string, Vector3>();
                Dictionary<string, Quaternion> dq = new Dictionary<string, Quaternion>();
                for (int i = 0; i < bonesCount; i++)
                {
                    line = sr.ReadLine();
                    string[] tl = line.Split(' ');
                    if (tl.Length == 4)
                    {
                        Vector3 tv = new Vector3(float.Parse(tl[1]), float.Parse(tl[2]), float.Parse(tl[3]));
                        dv.Add(tl[0], tv);
                        dq.Add(tl[0], Quaternion.EulerAngles(tv));
                    }
                }
                tpose_frame.euler = dv;
                tpose_frame.localRot = dq;
            }

            // Frames
            while ((line = sr.ReadLine()) != null)
            {
                int idx = Int32.Parse(line.Split(':')[1]);

                frame tf = new frame();
                Dictionary<string, Vector3> te = new Dictionary<string, Vector3>();
                Dictionary<string, Quaternion> tr = new Dictionary<string, Quaternion>();

                for (int i = 0; i < bonesCount; i++)
                {
                    string[] info = null;
                    info = (line = sr.ReadLine()).Split(' ');
                    if (info.Length != 4)
                        continue;

                    Vector3 tv = new Vector3(float.Parse(info[1]), float.Parse(info[2]), float.Parse(info[3]));

                    te.Add(info[0], tv);
                    tr.Add(info[0], Quaternion.EulerAngles(tv));
                }
                tf.euler = te;
                tf.localRot = tr;
                frames.Add(tf);
            }
        }
    }

    public Dictionary<string, Quaternion> getTpose()
    {
        Dictionary<string, Quaternion> boneData = new Dictionary<string, Quaternion>();
        Dictionary<string, Quaternion> localrot = tpose_frame.localRot;

        boneData.Add("Hips", eul2quat(0,0,0));

        foreach (BRTBone bb in boneList)
        {
            if (bb.name != "Hips")
            {
                boneData.Add(bb.name, boneData[Hierachy[bb.name]] * localrot[bb.name]);
            }
        }

        return boneData;
    }

    public Dictionary<string, Quaternion> getTFrame()
    {
        Dictionary<string, string> hierachy = getHierachy();
        Dictionary<string, Quaternion> boneData = new Dictionary<string, Quaternion>();
        Dictionary<string, Vector3> tposRot = new Dictionary<string, Vector3>{
            {"LeftUpLeg", new Vector3(0.0f, 0.0f, -21.0f)},
            {"RightUpLeg", new Vector3(0.0f, 0.0f, 21.0f)},
            {"Neck", new Vector3(-16.0f, 0.0f, 0.0f)},
            {"Neck1", new Vector3(21.0f, 0.0f, 0.0f)},
            {"Head", new Vector3(11.0f, 0.0f, 0.0f)},
            {"LeftArm", new Vector3(0.0f, 0.0f, -8.0f)},
            {"RightArm", new Vector3(0.0f, 0.0f, 8.0f)},
        };

        boneData.Add(boneList[0].name, eul2quat(0, 0, 0));
        foreach (BRTBone bb in boneList)
        {
            if (bb.name != boneList[0].name)
            {
                if (tposRot.ContainsKey(bb.name))
                {
                    Quaternion localrot = eul2quat(tposRot[bb.name].x, tposRot[bb.name].y, tposRot[bb.name].z);   // Euler Angle (z,y,x) to quaternion
                    boneData.Add(bb.name, boneData[hierachy[bb.name]] * localrot);
                }
                else
                {
                    Quaternion localrot = eul2quat(0, 0, 0);
                    boneData.Add(bb.name, boneData[hierachy[bb.name]] * localrot);
                }
            }
        }
        return boneData;
    }

    public Dictionary<string, Vector3> getOffset(float ratio)
    {
        Dictionary<string, Vector3> offset = new Dictionary<string, Vector3>();
        foreach (BRTBone bb in boneList)
        {
            offset.Add(bb.name, new Vector3(bb.offsetX * ratio, bb.offsetY * ratio, bb.offsetZ * ratio));
        }
        return offset;
    }

    public Dictionary<string, string> getHierachy()
    {
        Dictionary<string, string> hierachy = new Dictionary<string, string>();
        foreach (BRTBone bb in boneList)
        {
            foreach (BRTBone bbc in bb.children)
            {
                hierachy.Add(bbc.name, bb.name);
            }
        }
        return hierachy;
    }

    public Dictionary<string, Quaternion> getGlobalFrame(int frameidx)
    {
        Dictionary<string, string> hierachy = getHierachy();
        Dictionary<string, Quaternion> boneData = new Dictionary<string, Quaternion>();
        Dictionary<string, Quaternion> localrot = frames[frameidx].localRot;

        boneData.Add("Hips", eul2quat(0, 0, 0)); // root rot change to 0

        foreach (BRTBone bb in boneList)
        {
            if (bb.name != "Hips")
            {
                boneData.Add(bb.name, boneData[Hierachy[bb.name]] * localrot[bb.name]);
            }
        }

        return boneData;
    }

    public Quaternion eul2quat(float x, float y, float z)
    {
        return Quaternion.EulerAngles(new Vector3(x, y, z));
    }

    

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}
